#include<pthread.h>
#include<queue>
#include<iostream>
#include<unistd.h>
#include<functional>
int cap=5;//交换区的大小
using namespace std;
using task_t=function<void()>;
void download()
{
    cout<<"这是一个下载任务"<<endl;
    sleep(3);//下载耗时三秒钟
}
void upload()
{
    cout<<"这是一个上传任务"<<endl;
    sleep(3);
}
void log()
{
    cout<<"这是一个日志任务"<<endl;
    sleep(1);
}
void print()
{
    cout<<"这是一个打印任务"<<endl;
    sleep(1);
}
//阻塞队列
template<typename T>
class BlockQueue
{
public:
    BlockQueue(int cap)
    :_cap(cap)
    ,_csleep(0)
    ,_psleep(0)
    {
        pthread_mutex_init(&_mutex,nullptr);
        pthread_cond_init(&_empty,nullptr);
        pthread_cond_init(&_full,nullptr);
    }
    bool IsFull()
    {
        return _cap==_q.size();
    }
    bool IsEmpty()
    {
        return _q.empty();
    }
    void Equeue(T data)//入队列
    {
        pthread_mutex_lock(&_mutex);//申请锁成功了,则可以进入临界区生产资源
        while(IsFull())//判断是否为满
        {
            // 应该让生产者线程进行等待
            // 重点1：pthread_cond_wait调用成功，挂起当前线程之前，要先自动释放锁！！
            // 重点2：当线程被唤醒的时候，默认就在临界区内唤醒！要从pthread_cond_wait
            // 成功返回，需要当前线程，重新申请_mutex锁！！！
            // 重点3：如果我被唤醒，但是申请锁失败了？？我就会在锁上阻塞等待！！！
            _psleep++;
            pthread_cond_wait(&_full,&_mutex);//是满的,那么就挂起等待
            _psleep--;
            // 问题1: pthread_cond_wait是函数吗？有没有可能失败？pthread_cond_wait立即返回了
            // 问题2：pthread_cond_wait可能会因为，条件其实不满足，pthread_cond_wait 伪唤醒
        }
        //此时就不是满的了,就可以入队列了
        _q.push(data);
        //唤醒消费者
        if(_csleep>0)
        {
            pthread_cond_signal(&_empty);
            cout<<"唤醒一个消费者"<<endl;
        }
        pthread_mutex_unlock(&_mutex);
    }
    T Pop()//出队列
    {
        pthread_mutex_lock(&_mutex);
        while(IsEmpty())//判断队列是否为空
        {
            _csleep++;
            pthread_cond_wait(&_empty,&_mutex);
            _csleep--;
        }
        T data=_q.front();
        _q.pop();
        //唤醒生产者
        if(_psleep>0)
        {
            pthread_cond_signal(&_full);
            cout<<"唤醒一个生产者"<<endl;
        }
       pthread_mutex_unlock(&_mutex);
       return data;
    }
    ~BlockQueue()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_empty);
        pthread_cond_destroy(&_full);
    }
private:
    queue<T> _q;//储存临界资源
    pthread_mutex_t _mutex;
    pthread_cond_t _empty;
    pthread_cond_t _full;
    int _csleep;//消费者睡眠的个数
    int _psleep;//生产者睡眠的个数
    int _cap;
};